﻿#region copyright
//                Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
//           (See accompanying file LICENSE.txt or 
//     copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Collections.Generic;

namespace CSFP.Memoize
{
    /// <summary>
    /// Calculates a list which has only the values from the IsReady sources.
    /// </summary>
    public sealed class ResultList<T> : IDependent, IEnumerable<Tuple<int, T>>
    {
        struct ResultInfo
        {
            public IValue<T> Source;
            public int NextIndex; // doubly linked ring of IsReady sources, -1 means not in ring
            public int PrevIndex;
        }

        readonly ResultInfo[] _list;
        int _head;
        int _count;

        void LinkTail(int index)
        {
            if (_head >= 0) {
                int next = _head;
                int prev = _list[_head].PrevIndex;
                _list[index].NextIndex = next;
                _list[index].PrevIndex = prev;
                _list[prev].NextIndex = index;
                _list[next].PrevIndex = index;
            } else {
                _head = index;
                _list[index].NextIndex = index;
                _list[index].PrevIndex = index;
            }
            ++_count;
        }

        void Unlink(int index)
        {
            if (_list[index].NextIndex == index) {
                _head = -1;
                _list[index].NextIndex = -1;
                _list[index].PrevIndex = -1;
            } else {
                int next = _list[index].NextIndex;
                int prev = _list[index].PrevIndex;
                _list[next].PrevIndex = prev;
                _list[prev].NextIndex = next;
                _list[index].NextIndex = -1;
                _list[index].PrevIndex = -1;
                _head = next;
            }
            --_count;
        }

        public ResultList(IList<IValue<T>> sourceList)
        {
            _count = 0;
            _head = -1;
            _list = new ResultInfo[sourceList.Count];
            for (int i = 0; i < sourceList.Count; ++i) {
                _list[i].Source = sourceList[i];
                _list[i].Source.AddDependent(this, i);
                _list[i].NextIndex = -1;
                _list[i].PrevIndex = -1;
                if (_list[i].Source.IsReady)
                    LinkTail(i);
            }
        }

        void IDependent.Notify(int readyChange, int key)
        {
            if (readyChange == DependentList.BecomeReady) {
                LinkTail(key);
            } else if (readyChange == DependentList.BecomeNotReady) {
                Unlink(key);
            }
        }

        public struct Enumerator : IEnumerator<Tuple<int, T>>
        {
            readonly ResultList<T> _list;
            int _index;

            public Enumerator(ResultList<T> list)
            {
                _list = list;
                _index = -1;
            }

            public Tuple<int, T> Current
            {
                get { return Tuple.Create(_index, _list._list[_index].Source.Value); }
            }

            public bool MoveNext()
            {
                if (_index >= 0) {
                    _index = _list._list[_index].NextIndex;
                    return _index != _list._head;
                } else {
                    _index = _list._head;
                    return _index >= 0;
                }
            }

            public void Dispose() { }
            object System.Collections.IEnumerator.Current { get { return Current; } }
            public void Reset() { throw new NotImplementedException(); }
        }

        public int Count { get { return _count; } }
        IEnumerable<Tuple<int, T>> Value { get { return this; } }

        public IEnumerator<Tuple<int, T>> GetEnumerator() { return new Enumerator(this); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
}
